from datetime import datetime
import logging
import uuid
from flask_babel import _
from controllers.errors.online_mall.shop_errors import HProdParamError, HProductNotFoundError, HSKUNotFoundError, HSKUParamError, HShopNotFoundError
from core.entities.account_role import AccountWrap
from extensions.ext_database import db
from libs.helper import StrBoolNone
from models.online_mall.delivery import MTransport
from models.online_mall.prod_category import MProdCategory
from models.online_mall.product_cart import (
    MProduct,
    MProdSKU,
    ProdcutStatus,
)
from werkzeug.exceptions import InternalServerError
from models.online_mall.shop_msg import MShop
from services.errors.account import NotLoginError
from services.file_service import FileService

class ProductService:
    @staticmethod
    def get_product_list(account: AccountWrap, args: dict) -> dict:
        try:
            
            prod_id = args.get("id")
            total = 0
            has_more = False
            
            if prod_id:
                query = db.session.query(MProduct).filter(MProduct.id == prod_id)
                total = query.count()
            else:
                query = db.session.query(MProduct)
                if 'name' in args and args['name']:
                    query = query.filter(MProduct.name.ilike(f"%{args['name']}%"))
                if 'shop_id' in args and args['shop_id']:
                    query = query.filter(MProduct.shop_id == args['shop_id'])
                if 'max_price' in args and args['max_price'] is not None:
                    max_price_cents = int(float(args['max_price']) * 1000)
                    # 确保仅在max_price存在时应用过滤器
                    query = query.filter(MProduct.price <= max_price_cents)
                if 'min_price' in args and args['min_price'] is not None:
                    min_price_cents = int(float(args['min_price']) * 1000)
                    # 确保仅在min_price存在时应用过滤器
                    query = query.filter(MProduct.price >= min_price_cents)
                if 'status' in args and args['status']:
                    query = query.filter(MProduct.status == args['status'])
                if 'category_id' in args and args['category_id']:
                    query = query.filter(MProduct.category_id == args['category_id'])
                if 'max_sold_num' in args and args['max_sold_num']:
                    query = query.filter(MProduct.sold_num <= args['max_sold_num'])
                if 'min_sold_num' in args and args['min_sold_num']:
                    query = query.filter(MProduct.sold_num >= args['min_sold_num'])
                if 'max_stocks' in args and args['max_stocks']:
                    query = query.filter(MProduct.total_stocks <= args['max_stocks'])
                if 'min_stocks' in args and args['min_stocks']:
                    query = query.filter(MProduct.total_stocks >= args['min_stocks'])
                if 'beg_putaway_time' in args and args['beg_putaway_time']:
                    query = query.filter(MProduct.putaway_at >= args['beg_putaway_time'])
                if 'end_putaway_time' in args and args['end_putaway_time']:
                    query = query.filter(MProduct.putaway_at <= args['end_putaway_time'])
                if 'hasUserPickUp' in args and args['hasUserPickUp'] is not None:
                    query = query.filter(MProduct.delivery_mode.op('@>')({'hasUserPickUp': args['hasUserPickUp']}))
                if  'hasShopDelivery' in args and args['hasShopDelivery'] is not None:
                    query = query.filter(MProduct.delivery_mode.op('@>')({'hasShopDelivery': args['hasShopDelivery']}))
                total = query.count()
                if 'page' in args and args['page']>0 and 'page_size' in args and args['page_size']>0:
                    query = query.offset((args['page']-1)*args['page_size']).limit(args['page_size'])
                    has_more = total > args['page']*args['page_size']
              

            # 执行查询并获取结果
            products = query.all()
            # 返回结果字典
            return {
                "total": total,
                "has_more": has_more,
                "data": products
            }
        except Exception as e:
            # 记录异常信息
            logging.exception(e)
            return {
                "total": 0,
                "has_more": False,
                "data": [],
                "message":str(e)
            }
            

    @staticmethod
    def create_product(account:AccountWrap,args:dict)->MProduct:
        if not account or not isinstance(account,AccountWrap):
            raise NotLoginError()
        new_product = MProduct(
            id = str(uuid.uuid4()), # type:ignore
            status= ProdcutStatus.NORMAL, #type:ignore
            created_by=account.id #type:ignore
            )

        _,_,new_skus = ProductService._fill_prod_params(account,args, new_product)

        try:
            if new_skus:
                for new_ku in new_skus:
                    db.session.add(new_ku)
            db.session.add(new_product)
            db.session.commit()
            return new_product
        except Exception as e:
            db.session.rollback()
            raise InternalServerError(str(e))


    @staticmethod
    def update_product(account:AccountWrap,args:dict)->MProduct:
        if not account or not isinstance(account,AccountWrap):
            raise NotLoginError()
        product = db.session.get(MProduct,args.get('id'))
        if not product:
            raise HProductNotFoundError(args.get('id'))

        sku_ids,_,new_skus = ProductService._fill_prod_params(account,args, product)
        
        try:
            if new_skus:
                for new_sku in new_skus:
                    db.session.add(new_sku)
            db.session.query(MProdSKU).filter(~MProdSKU.id.in_(sku_ids),MProdSKU.prod_id==product.id).delete()
            product.updated_by = account.id
            product.updated_at = datetime.now()
            db.session.commit()
            return product
        except Exception as e:
            db.session.rollback()
            raise InternalServerError(str(e))
        
    @staticmethod
    def delete_product(account:AccountWrap,prod_id:str):
        if not account or not isinstance(account,AccountWrap):
            raise NotLoginError()
        product = db.session.get(MProduct,prod_id)
        if not product:
            raise HProductNotFoundError(prod_id)        
        try:
            db.session.delete(product)
            db.session.query(MProdSKU).filter(MProdSKU.prod_id==prod_id).delete()
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            raise InternalServerError(str(e))

    @staticmethod
    def _fill_prod_params(account:AccountWrap,args:dict, product:MProduct)->tuple[list[str],list[MProdSKU],list[MProdSKU]]:
        shop_id = args.get('shop_id')
        if not shop_id:
            raise HShopNotFoundError(shop_id)
        shop = db.session.get(MShop,shop_id)
        if not shop:
            raise HShopNotFoundError(shop_id)
        product.shop_id = shop_id
        name = args.get('name')
        if not name:
            raise HProdParamError(_("Name can not be empty"))
        product.name = name
        if 'ori_price' in args and args['ori_price']:
            product.OriPrice = args['ori_price']
        if 'price' in args and args['price']:
            product.Price = args['price']
        if 'brief' in args and args['brief']:
            product.brief = args['brief']
        if 'content' in args and args['content']:
            product.content = args['content']
        if 'pic_file_id' in args and args['pic_file_id']:
            if FileService.file_exist(args['pic_file_id']):
                product.pic_file_id = args['pic_file_id']
            else:
                raise HProdParamError(_("Product picture file is not found"))
        if 'imgs' in args and args['imgs']:
            product.ImageIds = args['imgs']
        if 'putaway_at' in args and args['putaway_at']:
            product.putaway_at = args['putaway_at']
        if 'status' in args and args['status']:
            if args['status'] not in ProdcutStatus.choices():
                raise HProdParamError(_("Product status is not valid"))
            product.status = args['status']
            if product.status == ProdcutStatus.PUTAWAY:
                product.putaway_at = datetime.now()

        if 'category_id' in args and args['category_id']:
            category = db.session.get(MProdCategory,args['category_id'])
            if not category:
                raise HProdParamError(_("Product category is not found"))
            product.category_id = args['category_id']
        if 'sold_num' in args and args['sold_num']:
            product.sold_num = int(args['sold_num'])
        if 'total_stocks' in args and args['total_stocks']:
            product.total_stocks = int(args['total_stocks'])
        if 'hasUserPickUp' in args and args['hasUserPickUp'] is not None:
            product.hasUserPickUp = args['hasUserPickUp']
        if 'hasShopDelivery' in args and args['hasShopDelivery'] is not None:
            product.hasShopDelivery = args['hasShopDelivery']
        if 'delivery_template_id' in args and args['delivery_template_id']:
            delivery_template = db.session.get(MTransport,args['delivery_template_id'])
            if not delivery_template:
                raise HProdParamError(_("Delivery template is not found"))
            product.delivery_template_id = args['delivery_template_id']

        return ProductService._file_sku_param(account, args, product)

    @staticmethod
    def _file_sku_param(account:AccountWrap, args:dict, product:MProduct)->tuple[list[str],list[MProdSKU],list[MProdSKU]]:
        sku_list = args.get('sku_list',[])
        new_skus = []
        sku_ids = []
        exit_skus = []
        for sku_dict in sku_list:
            sku_id = sku_dict.get('sku_id')

            if sku_id and sku_id.strip() != "":
                sku = db.session.get(MProdSKU,sku_id)
                if not sku:
                    raise HSKUNotFoundError(sku_id)
                sku.updated_at = datetime.now()
                sku.updated_by = account.id
                exit_skus.append(sku)
                sku_ids.append(sku_id)
            else:
                new_sku_id = str(uuid.uuid4())
                sku = MProdSKU(
                    id = new_sku_id, # type:ignore
                    status = True, # type:ignore
                    created_by = account.id, # type:ignore
                    properties = {} # type:ignore
                )
                new_skus.append(sku)
                sku_ids.append(new_sku_id)

            sku.prod_id = product.id
            if 'ori_price' in sku_dict and sku_dict['ori_price']:
                sku.OriPrice = float(sku_dict['ori_price'])
            if 'price' in sku_dict and sku_dict['price']:
                sku.Price = float(sku_dict['price'])
            if 'stocks' in sku_dict and sku_dict['stocks']:
                sku.stocks = int(sku_dict['stocks'])
            if 'actual_stocks' in sku_dict and sku_dict['actual_stocks']:
                sku.actual_stocks = int(sku_dict['actual_stocks'])
            if 'party_code' in sku_dict and sku_dict['party_code']:
                sku.party_code = sku_dict['party_code']
            if 'model_code' in sku_dict and sku_dict['model_code']:
                sku.model_code = sku_dict['model_code']
            if 'pic_file_id' in sku_dict and sku_dict['pic_file_id']:
                if FileService.file_exist(sku_dict['pic_file_id']):
                    sku.pic_file_id = sku_dict['pic_file_id']
                else:
                    raise HSKUParamError(_("sku pic file is not found"))
            if 'sku_name' in sku_dict and sku_dict['sku_name']:
                sku.sku_name = sku_dict['sku_name']
            if 'prod_name' in sku_dict and sku_dict['prod_name']:
                sku.prod_name = sku_dict['prod_name']
            if 'weight' in sku_dict and sku_dict['weight']:
                sku.weight = sku_dict['weight']
            if 'volume' in sku_dict and sku_dict['volume']:
                sku.volume = sku_dict['volume']
            if 'status' in sku_dict and sku_dict['status'] is not None:
                status = StrBoolNone(sku_dict['status'])
                if status is not None:
                    sku.status = status
            if 'is_deleted' in sku_dict and sku_dict['is_deleted'] is not None:
                is_deleted = StrBoolNone(sku_dict['is_deleted'])
                if is_deleted is not None:
                    sku.status = is_deleted
        return sku_ids,exit_skus,new_skus